package com.kstudio.videocapture.gl;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.opengl.GLUtils;
import android.text.Layout;
import android.text.TextPaint;
import android.util.FloatMath;

public final class ImageGL2D {

    private FloatBuffer mVertexBuffer;
    private FloatBuffer mTextureBuffer;
    private ByteBuffer  mIndexBuffer;
    private int mIndiceslength;

    private int[] mTextures = new int[1];
    private boolean initTextures;

    private int mHeight;
    private int mWidth;
    private int mLeft;
    private int mTop;
    private int mRight;
    private int mBottom;

    private float mTranslateX;
    private float mTranslateY;

    private float mScaleX;
    private float mScaleY;

    private ImageGL2D() {
        mHeight = 0;;
        mWidth = 0;

        mLeft = 0;
        mTop = 0;
        mRight = 0;
        mBottom = 0;

        mTranslateX = 0;
        mTranslateY = 0;

        mScaleX = 1f;
        mScaleY = 1f;

        initTextures = false;
    }

    public static ImageGL2D create() {
        ImageGL2D draw2D = new ImageGL2D();

        return draw2D;
    }

    public static ImageGL2D createFromBmp(GL10 gl, Bitmap bitmap) {
        ImageGL2D draw2D = new ImageGL2D();
        draw2D.loadTexture(gl, bitmap);

        return draw2D;
    }

    public static ImageGL2D createFromId(GL10 gl, Resources res, int id) {
        ImageGL2D draw2D = new ImageGL2D();
        draw2D.loadTexture(gl, res, id);

        return draw2D;
    }

    public static ImageGL2D createFromText(GL10 gl, String text, int size) {
        ImageGL2D draw2D = new ImageGL2D();
        draw2D.loadTexture(gl, text, size);

        return draw2D;
    }

    public void draw(GL10 gl) {
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();
        gl.glPushMatrix();

        gl.glScalef(mScaleX, mScaleY, 1f);
        gl.glTranslatef(mTranslateX, mTranslateY, -1f);

        gl.glMatrixMode(GL10.GL_TEXTURE);
        gl.glLoadIdentity();

        // Draw
        gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextures[0]);

        gl.glFrontFace(GL10.GL_CCW);
        gl.glEnable(GL10.GL_CULL_FACE);
        gl.glCullFace(GL10.GL_BACK);

        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

        gl.glVertexPointer(2, GL10.GL_FLOAT, 0, mVertexBuffer);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureBuffer);

        gl.glDrawElements(GL10.GL_TRIANGLES, mIndiceslength, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);

        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glDisable(GL10.GL_CULL_FACE);
        // Draw

        gl.glPopMatrix();
        gl.glLoadIdentity();
    }

    public void move(int x, int y) {
        mTranslateX = x;
        mTranslateY = y;
    }

    public void move(float x, float y) {
        mTranslateX = x;
        mTranslateY = y;
    }

    public void scale(float x, float y) {
        mScaleX = x;
        mScaleY = y;
    }

    public boolean checkIncludePosition(int x, int y) {
        if((getLeft() <= x) && (getRight() >= x) && (getTop() >= y) && (getBottom() <= y)) {
            return true;
        }
        return false;
    }

    public int getHeight() {
        return mHeight;
    }

    public int getWidth() {
        return mWidth;
    }

    public int getLeft() {
        return mLeft + (int) mTranslateX;
    }

    public int getRight() {
        return mRight + (int) mTranslateX;
    }

    public int getTop() {
        return mTop + (int) mTranslateY;
    }

    public int getBottom() {
        return mBottom + (int) mTranslateY;
    }

    public float getTranslateX() {
        return mTranslateX;
    }

    public float getTranslateY() {
        return mTranslateY;
    }

    public float getScaleX() {
        return mScaleX;
    }

    public float getScaleY() {
        return mScaleY;
    }

    public void setLayout(int l, int r, int b, int t) {
        mLeft   = l;
        mTop    = t;
        mRight  = r;
        mBottom = b;
        mHeight = t - b;
        mWidth  = r - l;

        float vertices[] = {
            (float) l, (float) b,
            (float) r, (float) b,
            (float) r, (float) t,
            (float) l, (float) t
        };

        float texture[] = { // texture reverse array
            0.0f, 1.0f,
            1.0f, 1.0f,
            1.0f, 0.0f,
            0.0f, 0.0f,
        };

        byte indices[] = {
            0,1,2,
            0,2,3
        };

        mIndiceslength = indices.length;

        ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);
        byteBuf.order(ByteOrder.nativeOrder());
        mVertexBuffer = byteBuf.asFloatBuffer();
        mVertexBuffer.put(vertices);
        mVertexBuffer.position(0);

        byteBuf = ByteBuffer.allocateDirect(texture.length * 4);
        byteBuf.order(ByteOrder.nativeOrder());
        mTextureBuffer = byteBuf.asFloatBuffer();
        mTextureBuffer.put(texture);
        mTextureBuffer.position(0);

        mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
        mIndexBuffer.put(indices);
        mIndexBuffer.position(0);
    }

    public void changeVertices(int l, int r, int b, int t) {
        mLeft   = l;
        mTop    = t;
        mRight  = r;
        mBottom = b;
        mHeight = t - b;
        mWidth  = r- l;

        float vertices[] = {
            (float) l, (float) b,
            (float) r, (float) b,
            (float) r, (float) t,
            (float) l, (float) t
        };

        ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);
        byteBuf.order(ByteOrder.nativeOrder());
        mVertexBuffer = byteBuf.asFloatBuffer();
        mVertexBuffer.put(vertices);
        mVertexBuffer.position(0);
    }

    public void loadTexture(GL10 gl, Bitmap bitmap) {
        if(initTextures)
            changeTexture(gl, bitmap);
        else
            initTexture(gl, bitmap);
    }

    public void loadTexture(GL10 gl, Resources res, int id) {
        Bitmap bitmap = BitmapFactory.decodeResource(res, id);

        loadTexture(gl, bitmap);

        bitmap.recycle();
    }

    public void loadTexture(GL10 gl, String text, int size) {
        Bitmap bitmap;
        Canvas canvas;
        TextPaint paint;
        int textWidth;

        paint = new TextPaint();
        paint.setColor(Color.WHITE);
        paint.setTextSize(size);
        paint.setTextScaleX(1);
        paint.setAntiAlias(true);
        // paint.setStrokeWidth(2.0f);
        paint.setStyle(TextPaint.Style.STROKE);
        // paint.setShadowLayer(5.0f, 10.0f, 10.0f, Color.BLACK);

        textWidth = (int) FloatMath.ceil(Layout.getDesiredWidth(text, (TextPaint) paint));

        Bitmap.Config config = Bitmap.Config.ARGB_8888;
        bitmap = Bitmap.createBitmap(textWidth, size, config);
        canvas = new Canvas(bitmap);
        bitmap.eraseColor(0);
        canvas.drawColor(0x00ffffff);

        canvas.drawText(text, 0, size, paint);

        loadTexture(gl, bitmap);

        bitmap.recycle();
    }

    /* Private Function */
    private void initTexture(GL10 gl, Bitmap bitmap) {
        gl.glGenTextures(1, mTextures, 0);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextures[0]);

        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);

        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

        mHeight = bitmap.getHeight();
        mWidth = bitmap.getWidth();

        initTextures = true;
    }

    private void changeTexture(GL10 gl, Bitmap bitmap) {
        gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextures[0]);
        GLUtils.texSubImage2D(GL10.GL_TEXTURE_2D, 0, 0, 0, bitmap);

        mHeight = bitmap.getHeight();
        mWidth = bitmap.getWidth();
    }
}
